package top.lixunda.ecommerce.server.user.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.impl.DefaultClaims;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RestController;
import top.lixunda.commerce.common.core.entity.MessageObject;
import top.lixunda.commerce.common.core.utils.ImageCodeUtil;
import top.lixunda.common.utils.ObjectValidators;
import top.lixunda.common.validate.XumValid;
import top.lixunda.ecommerce.server.auth.api.feign.IFeignEmailAuthCode;
import top.lixunda.ecommerce.server.business.BaseController;
import top.lixunda.ecommerce.server.business.api.entity.BusinessConstants;
import top.lixunda.ecommerce.server.business.api.entity.BusinessKeyConstants;
import top.lixunda.ecommerce.server.business.api.entity.cache.*;
import top.lixunda.ecommerce.server.good.api.feign.IFeignStoreController;
import top.lixunda.ecommerce.server.user.api.entity.db.*;
import top.lixunda.ecommerce.server.user.api.entity.dto.*;
import top.lixunda.ecommerce.server.user.api.controller.IUserController;
import top.lixunda.ecommerce.server.user.dto.BindingAccountDTO;
import top.lixunda.ecommerce.server.user.dto.InsertUserDTO;
import top.lixunda.ecommerce.server.user.service.*;
import top.lixunda.ecommerce.server.user.utils.JwtTokenUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.time.LocalDateTime;

/**
 * @author Xumda
 * @version time:2019/11/19 15:02
 */
@SuppressWarnings({"FieldCanBeLocal", "unused", "WeakerAccess", "UnusedReturnValue"})
@Slf4j
@RestController
public class UserController extends BaseController implements IUserController {

    public static final String KEY_AUTH_CODE_REGISTER = "userController.authCode.register";

    public static final String KEY_AUTH_CODE_LOGIN = "userController.authCode.login";

    public static final String KEY_AUTH_CODE_BINDING = "userController.authCode.binding";

    public static final String KEY_AUTH_CODE_UNBIND = "userController.authCode.unbind";

    public static final String KEY_AUTH_CODE_CHANGE_PASS = "userController.authCode.changePassword";

    private final IUserService userService;

    private final IAccountService accountService;

    private final IUserDetailService userDetailService;

    private final IFeignEmailAuthCode feignEmailAuthCode;

    private final IAccountBindingService accountBindingService;

    private final ILogChangePassService logChangePassService;

    private final HttpServletRequest request;

    private final HttpServletResponse response;

    private final ObjectMapper objectMapper;

    private final IFeignStoreController storeController;

    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    @Autowired
    public UserController(IUserService userService,
                          IAccountService accountService,
                          IUserDetailService userDetailService,
                          IFeignEmailAuthCode feignEmailAuthCode,
                          IAccountBindingService accountBindingService,
                          ILogChangePassService logChangePassService,
                          HttpServletRequest request,
                          HttpServletResponse response,
                          HttpSession httpSession,
                          ObjectMapper objectMapper,
                          IFeignStoreController storeController) {
        this.userService = userService;
        this.accountService = accountService;
        this.userDetailService = userDetailService;
        this.feignEmailAuthCode = feignEmailAuthCode;
        this.accountBindingService = accountBindingService;
        this.logChangePassService = logChangePassService;
        this.request = request;
        this.response = response;
        this.objectMapper = objectMapper;
        this.storeController = storeController;
    }

    /**
     * 根据UserDetail信息创建并保存token信息
     *
     * @param userDetail 用户信息，只在密码登陆时调用
     * @return UserDetailDO，原对象
     */
    private UserDetailDO createTokenAndSave(UserDetailDO userDetail) {
        Claims claims = new DefaultClaims();
        claims.put("userType", userDetail.getUserType());
        String token = JwtTokenUtil.createToken("", String.valueOf(userDetail.getAccount()), -1, claims);
        userDetail.setToken(token);
        userDetail.setTokenTime(LocalDateTime.now());
        accountService.update(new LambdaUpdateWrapper<AccountDO>()
                .set(AccountDO::getToken, userDetail.getToken())
                .set(AccountDO::getTokenTime, userDetail.getTokenTime())
                .eq(AccountDO::getAccount, userDetail.getAccount())
                .eq(AccountDO::getUserType, userDetail.getUserType()));
        return userDetail;
    }

    /**
     * 通过token进行登录
     *
     * @param loginTokenDTO 登陆信息封装
     * @return UserDetail信息
     */
    @Override
    @GlobalTransactional
    public MessageObject<UserDetailDO> loginByToken(@XumValid LoginTokenDTO loginTokenDTO) {
        //0. 验证参数
        if (ObjectValidators.isEmpty(loginTokenDTO) || ObjectValidators.isEmpty(loginTokenDTO.getToken())) {
            return MessageObject.error("参数错误： token 不能为空", null);
        }
        //1. 验证token有效性
        if (JwtTokenUtil.isExpiration(loginTokenDTO.getToken())) {
            return MessageObject.error("token已过期，请重新登陆", null);
        }
        //2. 获取用户信息
        UserDetailDO userDetail = userDetailService.getOne(
                new LambdaQueryWrapper<UserDetailDO>()
                        .eq(UserDetailDO::getBindAccount, loginTokenDTO.getUsername())
                        .eq(UserDetailDO::getBindingTypeId, loginTokenDTO.getType())
                        .eq(UserDetailDO::getToken, loginTokenDTO.getToken())
                        .eq(UserDetailDO::getForbidden, BusinessConstants.FORBIDDEN_NORMAL), false);
        //3. 检验用户存在可用性
        if (ObjectValidators.isEmpty(userDetail) || ObjectValidators.isEmpty(userDetail.getAccount()) || userDetail.getAccount() <= 0) {
            return MessageObject.error("用户不存在或不可用", null);
        }
        //4. 检验密码错误锁定
        String msg = userService.isLock(userDetail);
        if (msg != null) {
            return MessageObject.error(UserDetailDO.class, msg);
        }
        //5. 保存日志
        userService.insertLoginLog(userDetail, true);
        //6. 保存用户信息到session中
        saveLoginUserToSession(userDetail);
        //7. 清理密码，返回结果
        UserDetailDO result = userDetail.copy();
        result.setPassword(null);
        return MessageObject.success(result).setMsg("登陆成功");
    }

    /**
     * 通过验证码登陆系统
     *
     * @param loginCodeDTO 验证码登陆DTO
     * @return UserDetailDO
     */
    @SuppressWarnings("Duplicates")
    @Override
    @GlobalTransactional
    public MessageObject<UserDetailDO> loginByCode(@XumValid LoginCodeDTO loginCodeDTO) {
        // 验证验证码
        authSendCode(KEY_AUTH_CODE_LOGIN, loginCodeDTO.getCode(), loginCodeDTO.getUsername(), loginCodeDTO.getType());
        // 根据账号和类型获取用户信息
        UserDetailDO userDetail = userDetailService.getOne(
                new LambdaQueryWrapper<UserDetailDO>()
                        .eq(UserDetailDO::getBindAccount, loginCodeDTO.getUsername())
                        .eq(UserDetailDO::getBindingTypeId, loginCodeDTO.getType()));
        log.debug("用户数据 {}", userDetail);
        // 验证用户存在及可用
        if (userDetail == null) {
            return MessageObject.error(UserDetailDO.class, "用户不存在");
        } else if (BusinessConstants.FORBIDDEN_NORMAL != userDetail.getForbidden()) {
            StringBuilder msg = new StringBuilder("该用户不可用");
            if (ObjectValidators.isNotEmpty(userDetail.getForbiddenDesc())) {
                msg.append(",");
            }
            msg.append(userDetail.getForbiddenDesc());
            return MessageObject.error(UserDetailDO.class, msg.toString());
        }
        //3. 验证用户是否允许登陆，如密码错误三次则不允许登陆
        String msg = userService.isLock(userDetail);
        if (msg != null) {
            return MessageObject.error(UserDetailDO.class, msg);
        }
        // 保存日志
        userService.insertLoginLog(userDetail, true);
        // 生成token并保存
        createTokenAndSave(userDetail);
        // 保存用户信息到session中
        saveLoginUserToSession(userDetail);
        // 清理密码，返回结果
        UserDetailDO result = userDetail.copy();
        result.setPassword(null);
        return MessageObject.success(result).setMsg("登陆成功");
    }

    /**
     * 账户密码登陆
     *
     * @param loginPassDTO 密码登陆DTO
     * @return UserDetailDO
     */
    @SuppressWarnings("Duplicates")
    @Override
    @GlobalTransactional
    public MessageObject<UserDetailDO> loginByPass(@XumValid LoginPassDTO loginPassDTO) {
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_LOGIN,
                loginPassDTO.getCode());
        //1. 根据账号和类型获取用户信息
        LambdaQueryWrapper<UserDetailDO> wrapper = new LambdaQueryWrapper<>();
        if (loginPassDTO.getType() == LoginPassDTO.TYPE_ACCOUNT) {
            wrapper.eq(UserDetailDO::getAccount, loginPassDTO.getUsername());
        } else {
            wrapper.eq(UserDetailDO::getBindAccount, loginPassDTO.getUsername())
                    .eq(UserDetailDO::getBindingTypeId, loginPassDTO.getType());
        }
        if (ObjectValidators.isNotEmpty(loginPassDTO.getUserType())) {
            wrapper.eq(UserDetailDO::getUserType, loginPassDTO.getUserType());
        }
        UserDetailDO userDetail = userDetailService.getOne(wrapper);
        log.debug("用户数据 {}", userDetail);
        //2. 验证用户存在及可用
        if (userDetail == null) {
            return MessageObject.error(UserDetailDO.class, "用户不存在");
        } else if (BusinessConstants.FORBIDDEN_NORMAL != userDetail.getForbidden()) {
            StringBuilder msg = new StringBuilder("该用户不可用");
            if (ObjectValidators.isNotEmpty(userDetail.getForbiddenDesc())) {
                msg.append(",");
            }
            msg.append(userDetail.getForbiddenDesc());
            return MessageObject.error(UserDetailDO.class, msg.toString());
        }
        //3. 验证用户是否允许登陆，如密码错误三次则不允许登陆
        String msg = userService.isLock(userDetail);
        if (msg != null) {
            return MessageObject.error(UserDetailDO.class, msg);
        }
        //4. 验证密码
        if (!loginPassDTO.getPassword().equals(userDetail.getPassword())) {
            userService.insertLoginLog(userDetail, false);
            return MessageObject.error(UserDetailDO.class, "密码错误");
        }
        //4. 保存日志
        userService.insertLoginLog(userDetail, true);
        //6. 生成token并保存
        createTokenAndSave(userDetail);
        //7. 保存用户信息到session中
        saveLoginUserToSession(userDetail);
        //8. 清理密码，返回结果
        UserDetailDO result = userDetail.copy();
        result.setPassword(null);
        return MessageObject.success(result).setMsg("登陆成功");
    }

    /**
     * 将登陆用户数据保存到session中
     *
     * @param userDetail UserDetailDO
     */
    private void saveLoginUserToSession(UserDetailDO userDetail) {
        accountService.saveLoginUserToSession(userDetail);
    }

    /**
     * 退出当前登录账户
     *
     * @return MessageObject
     */
    @Override
    @GlobalTransactional
    public MessageObject<Boolean> logout() {
        // 清除缓存信息
        getHttpSessionProxy().removeAttribute(BusinessKeyConstants.SESSION_CACHE_LOGIN_USER);
        getHttpSessionProxy().removeAttribute(BusinessKeyConstants.SESSION_CACHE_STORE);
        getHttpSessionProxy().removeAttribute(BusinessKeyConstants.SESSION_CACHE_STORE_USER);
        getHttpSessionProxy().removeAttribute(BusinessKeyConstants.SESSION_CACHE_ADMIN_USER);
        getHttpSessionProxy().removeAttribute(BusinessKeyConstants.SESSION_CACHE_USER);
        // 清除token信息
        CacheLoginUser cacheLoginUser = currentLoginUserCache();
        if (cacheLoginUser != null && cacheLoginUser.getAccount() != null) {
            accountService.update(new LambdaUpdateWrapper<AccountDO>()
                    .eq(AccountDO::getAccount, cacheLoginUser.getAccount())
                    .set(AccountDO::getTokenTime, LocalDateTime.now())
                    .set(AccountDO::getToken, ""));
        }
        return MessageObject.success("退出成功", true);
    }

    /**
     * 发送注册验证码
     *
     * @param sendAuthCodeDTO 请求验证DTO
     * @return Boolean
     */
    @Override
    @GlobalTransactional
    public MessageObject<Boolean> sendRegisterCode(@XumValid SendAuthCodeDTO sendAuthCodeDTO) {
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_REGISTER,
                sendAuthCodeDTO.getCode());
        // 发送验证码
        sendAuthCode(KEY_AUTH_CODE_REGISTER, "账号注册", sendAuthCodeDTO);
        // 返回结果
        return MessageObject.success("获取验证码成功", true);
    }

    /**
     * 发送登陆验证码
     *
     * @param sendAuthCodeDTO 请求验证DTO
     * @return Boolean
     */
    @Override
    public MessageObject<Boolean> sendLoginCode(@XumValid SendAuthCodeDTO sendAuthCodeDTO) {
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_LOGIN,
                sendAuthCodeDTO.getCode());
        // 发送验证码
        sendAuthCode(KEY_AUTH_CODE_LOGIN, "用户登录", sendAuthCodeDTO);
        // 返回结果
        return MessageObject.success("获取验证码成功", true);
    }

    /**
     * 发送账号绑定验证码
     *
     * @param sendAuthCodeDTO 请求验证DTO
     * @return Boolean
     */
    @Override
    public MessageObject<Boolean> sendBindingCode(@XumValid SendAuthCodeDTO sendAuthCodeDTO) {
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_BINDING,
                sendAuthCodeDTO.getCode());
        // 发送验证码
        sendAuthCode(KEY_AUTH_CODE_BINDING, "账号绑定", sendAuthCodeDTO);
        // 返回结果
        return MessageObject.success("获取验证码成功", true);
    }

    /**
     * 发送账号解绑验证码
     *
     * @param sendAuthCodeDTO 请求验证DTO
     * @return Boolean
     */
    @Override
    public MessageObject<Boolean> sendUnbindCode(@XumValid SendAuthCodeDTO sendAuthCodeDTO) {
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_UNBINDING,
                sendAuthCodeDTO.getCode());
        // 发送验证码
        sendAuthCode(KEY_AUTH_CODE_UNBIND, "解除绑定", sendAuthCodeDTO);
        // 返回结果
        return MessageObject.success("获取验证码成功", true);
    }

    /**
     * 发送更改密码验证码
     *
     * @param sendAuthCodeDTO 包含请求验证码需要的信息
     * @return Boolean 是否发送成功
     */
    @Override
    public MessageObject<Boolean> sendChangePassCode(@XumValid SendAuthCodeDTO sendAuthCodeDTO) {
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_CHANGE_PASS,
                sendAuthCodeDTO.getCode());
        // 发送验证码
        sendAuthCode(KEY_AUTH_CODE_CHANGE_PASS, "解除绑定", sendAuthCodeDTO);
        // 返回结果
        return MessageObject.success("获取验证码成功", true);
    }

    /**
     * 通过验证码注册
     *
     * @param registerCodeDTO 验证码注册DTO
     * @return 注册的账户 401 已经存在
     */
    @Override
    @GlobalTransactional
    public MessageObject<AccountDO> registerByCode(@XumValid RegisterCodeDTO registerCodeDTO) {
        // 验证验证码
        authSendCode(KEY_AUTH_CODE_REGISTER, registerCodeDTO.getCode(), registerCodeDTO.getUsername(), registerCodeDTO.getType());
        // 验证注册账户是否可用
        log.debug("验证账户是否可用");
        AccountBindingDO binding = userService.getBinding(registerCodeDTO.getType(), registerCodeDTO.getUsername());
        if (binding != null) {
            return new MessageObject<AccountDO>().setCode(401).setMsg("该账户已经注册，直接前往登陆");
        }
        // 插入信息
        log.debug("插入新用户信息 {}", registerCodeDTO);
        AccountDO account = userService.insertNewUser(InsertUserDTO.fromRegisterCodeDTO(registerCodeDTO));
        // 返回信息
        account.setPassword("");
        return MessageObject.success("注册成功!", account);
    }

    /**
     * 通过验证码绑定当前登陆账户，用户绑定手机已经邮箱
     *
     * @param bindAcctCodeDTO 验证码绑定账户DTO
     * @return 绑定信息
     */
    @Override
    @GlobalTransactional
    public MessageObject<AccountBindingDO> bindingByCode(@XumValid BindCodeDTO bindAcctCodeDTO) {
        // 验证验证码
        authSendCode(KEY_AUTH_CODE_BINDING, bindAcctCodeDTO.getCode(), bindAcctCodeDTO.getAccount(), bindAcctCodeDTO.getType());
        // 验证绑定账户是否可用
        AccountBindingDO binding = userService.getBinding(bindAcctCodeDTO.getType(), bindAcctCodeDTO.getAccount());
        if (binding != null) {
            return new MessageObject<AccountBindingDO>().setCode(401).setMsg("该账户已经注册，直接前往登陆");
        }
        //4. 绑定账户
        CacheUser userDetail = currentUserCacheNotNull();
        BindingAccountDTO bind = new BindingAccountDTO();
        bind.setSysAccount(userDetail.getAccount());
        bind.setBindAccount(bindAcctCodeDTO.getAccount());
        bind.setType(bindAcctCodeDTO.getType());
        binding = userService.bindingAccount(bind);
        return MessageObject.success(binding).setMsg("绑定成功");
    }

    /**
     * 通过验证登陆密码的形式解绑绑定账号
     *
     * @param unbindPasswordDTO 密码解绑DTO
     * @return MessageObject<Boolean>
     */
    @Override
    public MessageObject<Boolean> unbindByPass(@XumValid UnbindPasswordDTO unbindPasswordDTO) {
        CacheUser userDetail = currentUserCacheNotNull();
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_UNBINDING,
                unbindPasswordDTO.getCode());
        // 验证密码
        authPassword(unbindPasswordDTO.getPassword());

        // 判断是否能够解绑，若只存在一个绑定账户时不能进行解绑
        int count = accountBindingService.count(
                new LambdaQueryWrapper<AccountBindingDO>()
                        .eq(AccountBindingDO::getAccountId, userDetail.getAccount())
                        .eq(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_TRUE));
        if (count <= 1) {
            return getErrorMessageObject("解绑失败，当前账户只存在一个绑定账户，不能进行解绑操作");
        }
        boolean success = accountBindingService.update(
                new LambdaUpdateWrapper<AccountBindingDO>()
                        .eq(AccountBindingDO::getAccountId, userDetail.getAccount())
                        .eq(AccountBindingDO::getBindingTypeId, unbindPasswordDTO.getType())
                        .eq(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_TRUE)
                        .set(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_FALSE));
        return success ? MessageObject.success("解绑成功", true) :
                MessageObject.error("解绑失败，绑定账户可能不存在", false);
    }

    /**
     * 通过验证验证码的形式解绑绑定账号
     *
     * @param unbindCodeDTO 验证码解绑DTO
     * @return MessageObject<Boolean>
     */
    @Override
    public MessageObject<Boolean> unbindByCode(@XumValid UnbindCodeDTO unbindCodeDTO) {
        CacheUser userDetail = currentUserCacheNotNull();
        // 验证验证码
        authSendCode(KEY_AUTH_CODE_UNBIND, unbindCodeDTO.getCode(), unbindCodeDTO.getAccount(), unbindCodeDTO.getType());
        // 判断是否能够解绑，若只存在一个绑定账户时不能进行解绑
        int count = accountBindingService.count(
                new LambdaQueryWrapper<AccountBindingDO>()
                        .eq(AccountBindingDO::getAccountId, userDetail.getAccount())
                        .eq(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_TRUE));
        if (count <= 1) {
            return getErrorMessageObject("解绑失败，当前账户只存在一个绑定账户，不能进行解绑操作");
        }
        // 解绑
        boolean success = accountBindingService.update(
                new LambdaUpdateWrapper<AccountBindingDO>()
                        .eq(AccountBindingDO::getAccountId, userDetail.getAccount())
                        .eq(AccountBindingDO::getBindingTypeId, unbindCodeDTO.getType())
                        .eq(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_TRUE)
                        .set(AccountBindingDO::getUseable, AccountBindingDO.USEABLE_FALSE));
        return success ? MessageObject.success("解绑成功", true) :
                MessageObject.error("解绑失败，绑定账户可能不存在", false);
    }

    /**
     * 通过原密码修改密码
     *
     * @param changePassPassDTO 原密码修改密码DTO
     * @return 是否修改成功
     */
    @Override
    public MessageObject<Boolean> changePassByPass(@XumValid ChangePassPassDTO changePassPassDTO) {
        CacheUser userDetail = currentUserCacheNotNull();
        // 验证图片验证码
        authImageCode(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE_CHANGE_PASS,
                changePassPassDTO.getCode());
        // 验证密码
        authPassword(changePassPassDTO.getOldPass());
        // 保存日志
        CacheUser detailDO = currentUserCacheNotNull();
        logChangePassService.save(new LogChangePassDO().setAccount(detailDO.getAccount())
                .setOld(detailDO.getPassword()).setTime(LocalDateTime.now()).setNewPass(changePassPassDTO.getNewPass())
                .setVerifyType(BusinessConstants.USER_VERIFY_TYPE_PASS));

        // 修改密码
        boolean success = accountService.update(
                new LambdaUpdateWrapper<AccountDO>()
                        .eq(AccountDO::getAccount, userDetail.getAccount())
                        .eq(AccountDO::getForbidden, BusinessConstants.FORBIDDEN_NORMAL)
                        .set(AccountDO::getPassword, changePassPassDTO.getNewPass()));
        //退出当前登陆
        logout();

        return success ? MessageObject.success("修改密码成功", true) :
                MessageObject.error("修改密码失败", false);
    }

    /**
     * 通过验证码修改密码
     *
     * @param changePassCodeDTO 验证码修改密码DTO
     * @return 是否修改成功
     */
    @Override
    public MessageObject<Boolean> changePassByCode(@XumValid ChangePassCodeDTO changePassCodeDTO) {
        CacheUser userDetail = currentUserCacheNotNull();
        // 验证验证码
        authSendCode(KEY_AUTH_CODE_CHANGE_PASS, changePassCodeDTO.getCode(), changePassCodeDTO.getAccount(),
                changePassCodeDTO.getType());
        // 保存日志
        CacheUser detailDO = currentUserCacheNotNull();
        logChangePassService.save(new LogChangePassDO().setAccount(detailDO.getAccount())
                .setOld(detailDO.getPassword()).setTime(LocalDateTime.now()).setNewPass(changePassCodeDTO.getNewPass())
                .setVerifyType(changePassCodeDTO.getType() == ChangePassCodeDTO.TYPE_PHONE ?
                        BusinessConstants.USER_VERIFY_TYPE_PHONE
                        : BusinessConstants.USER_VERIFY_TYPE_EMAIL));

        // 修改密码
        boolean success = accountService.update(
                new LambdaUpdateWrapper<AccountDO>()
                        .eq(AccountDO::getAccount, userDetail.getAccount())
                        .eq(AccountDO::getForbidden, BusinessConstants.FORBIDDEN_NORMAL)
                        .set(AccountDO::getPassword, changePassCodeDTO.getNewPass()));
        //退出当前登陆
        logout();

        return success ? MessageObject.success("修改密码成功", true) :
                MessageObject.error("修改密码失败", false);
    }

    /**
     * 图片验证码，通用
     *
     * @param key 保存图片的缓存key
     */
    @Override
    public void imageCode(String key) {
        if (ObjectValidators.isEmpty(key)) {
            throw getAppException("图片缓存Key不能为空！");
        }
        ImageCodeUtil.ImageCodeResult result = createImageCodeAndSave(BusinessKeyConstants.SESSION_CACHE_CODE_IMAGE + key);
        try {
            ImageCodeUtil.output(result.getBufferedImage(), response.getOutputStream());
        } catch (IOException e) {
            log.error("返回图片验证码失败");
            e.printStackTrace();
        }
    }

}
